package cn.imatu.framework.security.submit;

import cn.hutool.crypto.digest.DigestUtil;
import cn.imatu.framework.exception.BaseError;
import cn.imatu.framework.exception.BizException;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * @author shenguangyang
 */
@Aspect
public class RepeatSubmitAspect {
    @Resource
    private RepeatSubmitDefinition repeatSubmitDefinition;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Around("@annotation(repeatSubmit)")
    public Object around(ProceedingJoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
        String keyPrefix = repeatSubmitDefinition.getKeyPrefix();
        String cacheKey;
        if (repeatSubmit.type() == RepeatSubmit.Type.PARAM) {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            Class<?> targetClass = method.getDeclaringClass();
            String paramStr = point.getArgs() == null ? "" : Arrays.toString(point.getArgs());
            cacheKey = keyPrefix + "::" + DigestUtil.md5Hex(targetClass.getName() + method.getName() + paramStr);
        } else if (repeatSubmit.type() == RepeatSubmit.Type.TOKEN) {
            String token = repeatSubmitDefinition.getTokenSupplier().get();
            if (StringUtils.isEmpty(token)) {
                throw new BizException("防重令牌不能为空");
            }
            cacheKey = keyPrefix + "::" + DigestUtil.md5Hex(token);
        } else {
            throw new BizException("尚不支持的防重类型 " + repeatSubmit.type());
        }

        Boolean ret = redisTemplate.opsForValue().setIfAbsent(cacheKey, "1", repeatSubmit.time(), TimeUnit.SECONDS);
        if (!Boolean.TRUE.equals(ret)) {
            BaseError baseError = repeatSubmitDefinition.getBaseError();
            if (baseError == null) {
                throw new BizException("请勿重复提交！");
            } else {
                throw new BizException(baseError);
            }
        }
        return point.proceed();
    }
}
